namespace DotNetCommon.Accessors
{
    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Reflection;

    /// <summary>
    /// 用于构建<see cref="ObjectAccessor"/>和<see cref="GenericAccessor{TInstance}"/>实例。
    /// <code>
    /// 下面的示例用来完整演示一个类属性的读写操作，首先准备要进行反射的类：
    /// public class Person
    /// {
    ///     public int Id { get; set; }
    ///     public string Name { get; set; }
    /// }
    /// public class Program
    /// {
    ///     static void Main(string[] args)
    ///     {
    ///         //创建Person类的属性反射操作对象(操作属性时,忽略大小写)
    ///         var accessor = Accessor.Build&lt;Person&gt;(true);
    ///         var person = new Person() { Id = 1, Name = "普通用户"};
    ///         //输出: 1
    ///         Console.WriteLine(accessor[person, "Id"]);
    ///         accessor[person, "name"] = "小明";
    ///         //输出: "小明"
    ///         Console.WriteLine(person.Name);
    ///         Console.ReadLine();
    ///     }
    /// }
    /// </code>
    /// </summary>
    public abstract class Accessor
    {
        private static ConcurrentDictionary<(Type type, bool ignoreCase, bool includeNonPublic), ObjectAccessor> _objectAccessorCache = new ConcurrentDictionary<(Type type, bool ignoreCase, bool includeNonPublic), ObjectAccessor>();
        private static ConcurrentDictionary<(Type type, bool ignoreCase, bool includeNonPublic), ObjectAccessor> _genericAccessorCache = new ConcurrentDictionary<(Type type, bool ignoreCase, bool includeNonPublic), ObjectAccessor>();
        /// <summary>
        /// 用户创建<see cref="Accessor"/>类型的实例
        /// </summary>
        /// <param name="type">
        /// 要进行反射的模型类型
        /// </param>
        /// <param name="ignoreCase">
        /// 反射模型属性时是否忽略大小写
        /// </param>
        /// <param name="includeNonPublic">
        /// 是否反射模型的非<c>public</c>方法
        /// </param>
        protected Accessor(IReflect type, bool ignoreCase, bool includeNonPublic)
        {
            Type = type;
            IgnoreCase = ignoreCase;
            IncludesNonPublic = includeNonPublic;

            Comparer = IgnoreCase ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal;

            var flags = BindingFlags.Public | BindingFlags.Instance;
            if (IncludesNonPublic)
            {
                flags = flags | BindingFlags.NonPublic;
            }

            //属性
            var props = Type.GetProperties(flags);
            Properties = new SortedList<string, PropertyInfo>(props.Length, Comparer);
            foreach (var prop in props)
            {
                Properties[prop.Name] = prop;
            }

            //字段
            var fileds = Type.GetFields(flags);
            Fields = new SortedList<string, FieldInfo>(fileds.Length, Comparer);
            foreach (var field in fileds)
            {
                //忽略属性的字段
                if (field.Name.EndsWith("__BackingField")) continue;
                Fields[field.Name] = field;
            }
        }

        /// <summary>
        /// 获取反射模型属性时用到的名称比较器 <see cref="StringComparer"/>
        /// </summary>
        protected StringComparer Comparer { get; }

        /// <summary>
        /// 当前反射的对象类型
        /// </summary>
        public IReflect Type { get; }

        /// <summary>
        /// 获取的属性是否是忽略大小的
        /// </summary>
        public bool IgnoreCase { get; }

        /// <summary>
        /// 获取的属性是否包含非public类型的
        /// </summary>
        public bool IncludesNonPublic { get; }

        /// <summary>
        /// 所有能获取到的属性信息
        /// </summary>
        public SortedList<string, PropertyInfo> Properties { get; }

        /// <summary>
        /// 所有能获取到的字段信息
        /// </summary>
        public SortedList<string, FieldInfo> Fields { get; }

        /// <summary>
        /// 根据给定的对象创建<see cref="ObjectAccessor"/>实例
        /// </summary>
        /// <param name="instance">给定的对象</param>
        /// <param name="ignoreCase">是否忽略大小写</param>
        /// <param name="includeNonPublic">是否包含非public属性</param>
        [DebuggerStepThrough]
        public static ObjectAccessor Build(object instance, bool ignoreCase = false, bool includeNonPublic = false)
        {
            Ensure.NotNull(instance, nameof(instance));
            return Build(instance.GetType(), ignoreCase, includeNonPublic);
        }

        /// <summary>
        /// 根据给定的对象类型创建<see cref="ObjectAccessor"/>实例
        /// </summary>
        /// <param name="instanceType">给定的对象类型</param>
        /// <param name="ignoreCase">是否忽略大小写</param>
        /// <param name="includeNonPublic">是否包含非public属性</param>
        [DebuggerStepThrough]
        public static ObjectAccessor Build(Type instanceType, bool ignoreCase = false, bool includeNonPublic = false)
        {
            Ensure.NotNull(instanceType, nameof(instanceType));
            return _objectAccessorCache.GetOrAdd((instanceType, ignoreCase, includeNonPublic), _ => new ObjectAccessor(instanceType, ignoreCase, includeNonPublic));
        }

        /// <summary>
        /// 根据给定的对象类型创建<see cref="GenericAccessor{TInstance}"/>实例
        /// </summary>
        /// <typeparam name="TInstance">对象类型</typeparam>
        /// <param name="ignoreCase">是否忽略大小写</param>
        /// <param name="includeNonPublic">是否包含非public属性</param>
        [DebuggerStepThrough]
        public static GenericAccessor<TInstance> Build<TInstance>(
            bool ignoreCase = false, bool includeNonPublic = false) where TInstance : class
        {
            return (GenericAccessor<TInstance>)_genericAccessorCache.GetOrAdd((typeof(TInstance), ignoreCase, includeNonPublic), _ => new GenericAccessor<TInstance>(ignoreCase, includeNonPublic));
        }
    }
}